package com.gree.auth.config.configuration;

import com.gree.framework.entity.CusUserDetails;
import com.gree.framework.entity.SysClient;
import com.gree.framework.service.SysClientService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class MyDefaultAccessTokenConverter extends DefaultAccessTokenConverter {

    private boolean includeGrantType;

    private String scopeAttribute = SCOPE;

    private String clientIdAttribute = CLIENT_ID;

    private MyUserAuthenticationConverter userTokenConverter = new MyUserAuthenticationConverter();

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SysClientService sysClientService;

    public MyDefaultAccessTokenConverter() {
        super();
    }


    public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        Map<String, Object> response = new HashMap<String, Object>();
        OAuth2Request clientToken = authentication.getOAuth2Request();

        if (!authentication.isClientOnly()) {
            response.putAll(userTokenConverter.convertUserAuthentication(authentication.getUserAuthentication()));
        } else {
            if (clientToken.getAuthorities()!=null && !clientToken.getAuthorities().isEmpty()) {
                response.put(MyUserAuthenticationConverter.AUTHORITIES,
                        AuthorityUtils.authorityListToSet(clientToken.getAuthorities()));
            }
        }

        if (token.getScope()!=null) {
            response.put(scopeAttribute, token.getScope());
        }
        if (token.getAdditionalInformation().containsKey(JTI)) {
            response.put(JTI, token.getAdditionalInformation().get(JTI));
        }

        if (token.getExpiration() != null) {
            response.put(EXP, token.getExpiration().getTime() / 1000);
        }

        if (includeGrantType && authentication.getOAuth2Request().getGrantType()!=null) {
            response.put(GRANT_TYPE, authentication.getOAuth2Request().getGrantType());
        }

        response.putAll(token.getAdditionalInformation());

        response.put(clientIdAttribute, clientToken.getClientId());
        if (clientToken.getResourceIds() != null && !clientToken.getResourceIds().isEmpty()) {
            response.put(AUD, clientToken.getResourceIds());
        }

        if(StringUtils.isNotBlank(token.getValue())){
            if(token != null){
                SysClient client =sysClientService.findByClientId(clientToken.getClientId());
                int exp = (int)((token.getExpiration().getTime()-new Date().getTime())/1000);
                if(exp<client.getAccessTokenValiditySeconds()/4){
                    AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
                    String key = authenticationKeyGenerator.extractKey(authentication);
                    RedisSerializer<String> redisSerializer = new StringRedisSerializer();
                    redisTemplate.setKeySerializer(redisSerializer);
                    redisTemplate.expire("auth_to_access:"+key,client.getAccessTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("access:"+token.getValue(),client.getAccessTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("auth:"+token.getValue(),client.getAccessTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("access_to_refresh:"+token.getValue(),client.getAccessTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("uname_to_access:"+getApprovalKey(authentication),client.getAccessTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("client_id_to_access:"+authentication.getOAuth2Request().getClientId(),client.getAccessTokenValiditySeconds().longValue(), TimeUnit.SECONDS);

                    redisTemplate.expire("refresh:"+authentication.getOAuth2Request().getClientId(),client.getRefreshTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("refresh_auth:"+authentication.getOAuth2Request().getClientId(),client.getRefreshTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("refresh_to_access:"+authentication.getOAuth2Request().getClientId(),client.getRefreshTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                    redisTemplate.expire("access_to_refresh:"+authentication.getOAuth2Request().getClientId(),client.getRefreshTokenValiditySeconds().longValue(), TimeUnit.SECONDS);
                }
            }
        }

        return response;
    }

    private static String getApprovalKey(OAuth2Authentication authentication) {
        String userName = authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication().getName();

        if(authentication.getUserAuthentication().getPrincipal() instanceof CusUserDetails){
            CusUserDetails cusUserDetails = (CusUserDetails) authentication.getUserAuthentication().getPrincipal();
            return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName,cusUserDetails.getComp());
        }


        return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
    }


    private static String getApprovalKey(String clientId, String userName) {
        return clientId + (userName == null ? "" : ":" + userName);
    }

    private static String getApprovalKey(String clientId, String userName, String comp) {
        return clientId + (userName == null ? "" : ":" + userName + ":" + comp);
    }
}
